package com.imachen.ymcc.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.imachen.ymcc.domain.Course;
import com.imachen.ymcc.domain.CourseMarket;
import com.imachen.ymcc.domain.KillActivity;
import com.imachen.ymcc.domain.KillCourse;
import com.imachen.ymcc.dto.KillOrderParamDto;
import com.imachen.ymcc.dto.KillPreOrderParamDto;
import com.imachen.ymcc.enums.GlobalExceptionCode;
import com.imachen.ymcc.fegin.CourseFeignClien;
import com.imachen.ymcc.mapper.KillCourseMapper;
import com.imachen.ymcc.result.JSONResult;
import com.imachen.ymcc.service.IKillActivityService;
import com.imachen.ymcc.service.IKillCourseService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.imachen.ymcc.util.AssertUtil;
import com.imachen.ymcc.util.CodeGenerateUtils;
import com.imachen.ymcc.vo.CourseInfoVo;
import com.imachen.ymcc.vo.CourseOrderVo;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author imachen
 * @since 2022-10-20
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private CourseFeignClien courseFeignClien;

    @Autowired
    private IKillActivityService killActivityService;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 加入秒杀
     * @param killCourse
     */
    @Override
    public void joinKill(KillCourse killCourse) {
        // 1.JSR303参数校验
        // 2.业务校验,判断该课程是否存在和上架
        JSONResult jsonResult = courseFeignClien.get(killCourse.getCourseId());
        AssertUtil.isTrue(jsonResult.isSuccess(), GlobalExceptionCode.COURSE_IS_NULL_ERROR);
        Object data = jsonResult.getData();
        AssertUtil.isNotNull(data,GlobalExceptionCode.COURSE_IS_NULL_ERROR);
        Course course = JSON.parseObject(JSON.toJSONString(data), Course.class);
        Boolean boo = course.getStatus() == Course.STATUS_ON;
        AssertUtil.isTrue(boo, GlobalExceptionCode.COURSE_OFF_LINE_ERROR);

        // 3.判断活动是否是下架状态
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        AssertUtil.isNotNull(killActivity, GlobalExceptionCode.KILL_ACTIVITY_IS_NULL_ERROR);

        Boolean isOK = killActivity.getPublishStatus() == KillActivity.WAIT_PUBLISH;
        AssertUtil.isTrue(isOK,GlobalExceptionCode.KILL_ACTIVITY_SUCCESS_PUBLISH_ERROR);
        // 4.判断此课程是否已经加入到秒杀活动中了
        KillCourse killCourseTmp = selectCourseActivity(killCourse.getActivityId(),killCourse.getCourseId());
        AssertUtil.isNull(killCourseTmp,GlobalExceptionCode.KILL_COURSE_NOT_NULL_ERROR);

        // 5.设置秒杀课程不存在的值
        initKillCourse(killCourse, killActivity);
        insert(killCourse);
    }

    /**
     * 查询redis中所有秒杀课程信息
     * @return
     */
    @Override
    public List<KillCourse> onlineAll() {
        List<KillCourse> killCourses = new ArrayList<>();
        Set<Object> keys = redisTemplate.keys("activity:*");
        keys.forEach(key -> {
            // values：根据大key直接拿到对应的小value
            List values = redisTemplate.opsForHash().values(key);
            killCourses.addAll(values);
        });
        return killCourses;
    }


    /**
     * 展示课程详情数据
     * @param killCourseId
     * @param killActivityId
     * @return
     */
    @Override
    public KillCourse onlineOne(Long killCourseId, Long killActivityId) {

        return (KillCourse) redisTemplate.opsForHash().get("activity:" + killActivityId,killCourseId.toString());
    }

    /**
     * 秒杀
     * @param killOrderParamDto
     * @return
     */
    @Override
    public String kill(KillOrderParamDto killOrderParamDto) {
        Long loginId = 3L;
        // 1.JSR303参数参数校验
        // 2.业务校验
        Long killActivityId = killOrderParamDto.getKillActivityId();
        Long killCourseId = killOrderParamDto.getKillCourseId();
        //  2.1 查询秒杀是否存在
        Object killCourseTmp = redisTemplate.opsForHash().get("activity:" + killActivityId, killCourseId.toString());
        AssertUtil.isNotNull(killCourseTmp, GlobalExceptionCode.KILL_COURSE_IS_NULL_ERROR);
        //  2.2 判断该秒杀是否在秒杀中
        KillCourse killCourse = JSON.parseObject(JSON.toJSONString(killCourseTmp), KillCourse.class);
        AssertUtil.isTrue(killCourse.getKilling(), GlobalExceptionCode.KILL_STOP_ERROR);
        //  2.3 判断每个用户 每个活动的每个商品只能下单一次
//        Object preOrderNoTmp = redisTemplate.opsForValue().get(loginId + ":" + killActivityId + ":" + killCourseId);
//        AssertUtil.isNull(preOrderNoTmp, GlobalExceptionCode.KILL_USER_ALREADY_ORDER_ERROR);

        // 3.扣减信号量
        RSemaphore semaphore = redissonClient.getSemaphore(killActivityId + ":" + killCourseId);
        boolean tryAcquire = semaphore.tryAcquire(1);
        AssertUtil.isTrue(tryAcquire,GlobalExceptionCode.KILL_FAIL_STOCK_IS_NULL_ERROR);

        // 4.创建预创订单
        KillPreOrderParamDto killPreOrderParamDto = new KillPreOrderParamDto();
        killPreOrderParamDto.setKillCourseId(killCourseId);
        killPreOrderParamDto.setKillActivityId(killActivityId);
        killPreOrderParamDto.setUserId(loginId);
        killPreOrderParamDto.setCourseId(killCourse.getCourseId());
        killPreOrderParamDto.setCourseName(killCourse.getCourseName());
        killPreOrderParamDto.setCoursePic(killCourse.getCoursePic());
        String orderNo = CodeGenerateUtils.generateOrderSn(loginId);
        killPreOrderParamDto.setOrderNo(orderNo);
        killPreOrderParamDto.setTotalAmount(killCourse.getKillPrice());
        killPreOrderParamDto.setTotalCount(1);

        // 5.将预创订单出入redis
        redisTemplate.opsForValue().set(orderNo, killPreOrderParamDto);

        // 6.当前用户秒杀成功后 记录唯一标识到redis
   //     redisTemplate.opsForValue().set(loginId + ":" + killActivityId + ":" + killCourseId, orderNo);


        return orderNo;
    }


    /**
     * 查询秒杀下单结算页数据
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrderVo killOnlineOneByOrder(String orderNo) {
        // 1.查询预创订单信息
        Object orderNoTemp = redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(orderNoTemp, GlobalExceptionCode.KILL_BEFORE_ORDER_IS_NULL_ERROR);
        KillPreOrderParamDto killPreOrderParamDto = JSON.parseObject(JSON.toJSONString(orderNoTemp), KillPreOrderParamDto.class);
        // 2.根据课程Id查询课程以及课程销售对象，并封装返回值
        CourseOrderVo courseOrderVo = new CourseOrderVo();
        List<CourseInfoVo> courseInfoVos = new ArrayList<>();
        CourseInfoVo courseInfoVo = new CourseInfoVo();

        Course course = new Course();
        course.setName(killPreOrderParamDto.getCourseName());
        course.setPic(killPreOrderParamDto.getCoursePic());

        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(killPreOrderParamDto.getTotalAmount());

        courseInfoVo.setCourse(course);
        courseInfoVo.setCourseMarket(courseMarket);
        // 设置课程所需的总金额
        courseOrderVo.setTotalAmount(killPreOrderParamDto.getTotalAmount());
        // 添加课程信息
        courseInfoVos.add(courseInfoVo);

        courseOrderVo.setCourseInfos(courseInfoVos);

        return courseOrderVo;
    }

    /**
     * 设置秒杀课程不存在的值
     * @param killCourse
     * @param killActivity
     */
    private void initKillCourse(KillCourse killCourse, KillActivity killActivity) {
        killCourse.setCreateTime(new Date());
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setKillLimit(1);
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setPublishStatus(killActivity.getPublishStatus());
    }

    /**
     * 根据课程Id+秒杀活动Id查询秒杀课程
     * @param activityId
     * @param courseId
     * @return
     */
    private KillCourse selectCourseActivity(Long activityId, Long courseId) {
        return selectOne(new EntityWrapper<KillCourse>()
                .eq("activity_id", activityId)
                .eq("course_id", courseId));
    }
}
